package cn.b504.qpcr.utils;

import java.util.ArrayList;
import java.util.List;

import static cn.b504.qpcr.utils.CalCqUtils.cqMinLimit;

/**
 * @author wyy ssc
 * @data 2019-02-02 13:49
 * @update 2021-04-08 16:54
 */
public class MathUtils {
  /* 求导数: 跳着求导
   * (1)-(0) / 1、   (2)-(0) / 2、  (3)-(1) / 2、
   * (4)-(2) / 2、   (5)-(3) / 2、  (6)-(4) / 2、(7)-(5) /2、
   * (n)-(n-2) / 2、 (n)-(n-1) / 1
   */
  public static List<Float> getDaoList_jumpOne(List<Float> list) {
    if (list.size() < 2) {
      return list;
    }

    List<Float> result = new ArrayList<>(list.size());
    result.add(list.get(1) - list.get(0));
    for (int i = 1; i < list.size() - 1; i++) {
      result.add((list.get(i + 1) - list.get(i - 1)) / 2);
    }
    result.add(list.get(list.size() - 1) - list.get(list.size() - 2));
    return result;
  }

  // 求导数: 第一位补零
  public static List<Float> getDaoList_fillZeroAtFirst(List<Float> list) {
    if (list.size() < 2) {
      return list;
    }

    List<Float> result = new ArrayList<>(list.size());
    result.add(0F);
    for (int i = 0; i < list.size() - 1; i++) {
      result.add(list.get(i + 1) - list.get(i));
    }
    return result;
  }

  // 求导数: 将第一位重复
  public static List<Float> getDaoList_repeatFirstOne(List<Float> list) {
    if (list.size() < 2) {
      return list;
    }

    List<Float> result = new ArrayList<>(list.size());
    result.add(list.get(1) - list.get(0));
    for (int i = 0; i < list.size() - 1; i++) {
      result.add(list.get(i + 1) - list.get(i));
    }
    return result;
  }

  // 求导数: 将最后一位重复
  public static List<Float> getDaoList_repeatLastOne(List<Float> list) {
    if (list.size() < 2) {
      return list;
    }

    List<Float> result = new ArrayList<>(list.size());
    for (int i = 0; i < list.size() - 1; i++) {
      result.add(list.get(i + 1) - list.get(i));
    }
    result.add(result.get(result.size() - 1));
    return result;
  }

  // 求导数: base on 3, 3 = (4 + 4 + 5) - (2 + 2 + 1)
  public static List<Float> getDaoList_baseOn3(List<Float> list) {
    if (list.size() < 5) {
      return list;
    }

    List<Float> result = new ArrayList<>(list.size());
    // 先扔进去俩
    result.add(0F);
    result.add(0F);
    // 求后面的（除了最后俩）
    for (int i = 2; i < list.size() - 2; i++) {
      result.add(((list.get(i + 1) * 2 + list.get(i + 2)) - (list.get(i - 1) * 2 + list.get(i - 2))) / 3);
    }
    // 将前俩和第三个重复
    result.set(0, result.get(2));
    result.set(1, result.get(2));
    // 将后俩和倒数第三个重复
    result.add(result.get(list.size() - 3));
    result.add(result.get(list.size() - 3));
    return result;
  }

  private static int getMaxIndex(List<Float> data, int rangeOfStartIndex, int rangeOfEndIndex) {
    double max = data.get(rangeOfStartIndex);
    for (int i = rangeOfStartIndex + 1; i < rangeOfEndIndex; i++) {
      if (data.get(i) - max > 0) {
        max = data.get(i);
        rangeOfStartIndex = i;
      }
    }
    return rangeOfStartIndex;
  }

  private static int getMinIndex(List<Float> data, int rangeOfStartIndex, int rangeOfEndIndex) {
    double min = data.get(rangeOfStartIndex);
    for (int i = rangeOfStartIndex + 1; i < rangeOfEndIndex; i++) {
      if (data.get(i) - min < 0) {
        min = data.get(i);
        rangeOfStartIndex = i;
      }
    }
    return rangeOfStartIndex;
  }

  public static int maxIndexSinceIndex3(List<Float> list) {
    return maxIndexSinceIndex3(list, list.size());
  }

  public static int maxIndexSinceIndex3(List<Float> list, int end) {
    if (list.size() < 4 || list.size() < end || end < 4) {
      return 0;
    }

    return getMaxIndex(list, 3, end);
  }

  public static int maxIndex(List<Float> list) {
    return maxIndex(list, list.size());
  }

  private static int maxIndex(List<Float> list, int end) {
    if (list.size() < end) {
      return 0;
    }

    return getMaxIndex(list, 0, end);
  }

  public static int minIndex(List<Float> list) {
    return minIndex(list, list.size());
  }

  public static int minIndex(List<Float> list, int end) {
    if (list.size() == 0 || list.size() < end) {
      return 0;
    }

    return getMinIndex(list, 0, end);
  }

  public static float getMin(List<Float> list) {
    if (list.size() == 0) {
      return 0;
    }
    float min = list.get(0);
    for (float num : list) {
      if (num < min) {
        min = num;
      }
    }
    return min;
  }

  public static double getMax(List<Float> list) {
    if (list.size() == 0) {
      return 0;
    }
    double max = list.get(0);
    for (double num : list) {
      if (num > max) {
        max = num;
      }
    }
    return max;
  }

  public static List<Float> oppositeNumber(List<Float> list) {
    List<Float> result = new ArrayList<>(list.size());
    for (Float data : list) {
      result.add(0 - data);
    }
    return result;
  }

  public static boolean isDoubleNumber(String str) {
    boolean flagDecimalPoint = false;
    for (int i = str.length(); --i >= 0; ) {
      int chr = str.charAt(i);
      if (chr == 45 && i != 0) {  // 负号
        return false;
      }
      if (str.length() == 1 && chr == 45) { // 只有一个负号
        return false;
      }
      if (chr == 46) {    // 小数点
        if (flagDecimalPoint) {
          return false;
        }
        flagDecimalPoint = true;
      }
      if (chr < 45 || chr == 47 || chr > 57) {
        return false;
      }
    }
    return true;
  }

  public static float getAverageOfList(List<Float> list, float defaultValue) {
    switch (list.size()) {
      case 0:
        return defaultValue;
      case 1:
        return list.get(0);
      default:
        double sum = 0;
        for (float d : list) {
          sum += d;
        }
        return (float) (sum / list.size());
    }
  }

  public static double[] wrapper2PrimitiveListDouble(List<Double> list) {
    double[] arr = new double[list.size()];
    for (int i = 0; i < list.size(); i++) {
      arr[i] = list.get(i);
    }
    return arr;
  }

  public static double[][] wrapper2PrimitiveListListDouble(List<List<Double>> list) {
    double[][] arr = new double[list.size()][];
    for (int i = 0; i < list.size(); i++) {
      arr[i] = wrapper2PrimitiveListDouble(list.get(i));
    }
    return arr;
  }

  public static double[] wrapper2PrimitiveDoubleArray(Double[] list) {
    double[] arr = new double[list.length];
    for (int i = 0; i < list.length; i++) {
      arr[i] = list[i];
    }
    return arr;
  }

  public static double[][] wrapper2PrimitiveListDoubleArray(List<Double[]> list) {
    double[][] arr = new double[list.size()][];
    for (int i = 0; i < list.size(); i++) {
      arr[i] = wrapper2PrimitiveDoubleArray(list.get(i));
    }
    return arr;
  }

  public static List<Float> backgroundRemoval(List<Float> list) {
    list = new ArrayList<>(list);

    float sum = 0, count = 0, average;
    // 本底去除
    for (int i = 4; i < 10; i++) {
      if (list.size() > i) {
        sum += list.get(i);
        count++;
      }
    }
    average = sum / count;
    // 拉平
    for (int i = 0; i < list.size(); i++) {
      list.set(i, list.get(i) - average);
    }

    // 前 aheadIndex 个数等同于第 aheadIndex + 1 个数
    int aheadIndex = Math.min(2, list.size() - 1);
    for (int i = 0; i < aheadIndex; i++) {
      list.set(i, list.get(aheadIndex));
    }
    return list;
  }

  public static float x2y(List<Float> list, float x) {
    if (list.size() == 0) {
      return 0;
    }
    if (x - Math.round(x) == 0 && x < list.size()) {
      return list.get((int) x);
    } else if (1 <= x && x <= list.size()) {
      int leftY = (int) Math.floor(x),
          rightY = (int) Math.ceil(x);
      float left = list.get(leftY - 1),
          right = list.get(rightY - 1);
      return ((x - leftY) * right - (x - rightY) * left) / (rightY - leftY);
    } else {
      return list.get(0);
    }
  }

  public static float y2x(List<Float> list, float y) {
    if (y == 0) {
      return -1F;
    }
    float x = -1F;
    for (int i = 1; i < list.size(); i++) {
      if (y - list.get(i - 1) >= 0 && y - list.get(i) <= 0 && list.get(i) - list.get(i - 1) != 0) {
        x = (y - list.get(i - 1)) / (list.get(i) - list.get(i - 1)) + i;
        break;
      }
    }
    if (x < cqMinLimit || list.size() < x) {
      return -1F;
    }
    return x;
  }

  public static int middle(int middle, int min, int max) {
    return Math.min(Math.max(middle, min), max);
  }

  public static long middle(long middle, long min, int max) {
    return Math.min(Math.max(middle, min), max);
  }

  public static float middle(float middle, float min, float max) {
    return Math.min(Math.max(middle, min), max);
  }

  public static double middle(double middle, double min, double max) {
    return Math.min(Math.max(middle, min), max);
  }

  public static double average(int a, int b, int c) {
    return averageWithWeight(a, b, c, 1);
  }

  public static double average(long a, long b, long c) {
    return averageWithWeight(a, b, c, 1);
  }

  public static float average(float a, float b, float c) {
    return averageWithWeight(a, b, c, 1);
  }

  public static double average(double a, double b, double c) {
    return averageWithWeight(a, b, c, 1);
  }

  public static double averageWithWeight(int a, int b, int c, int weight) {
    return (a + b * weight + c) / (weight + 2.0);
  }

  public static double averageWithWeight(long a, long b, long c, int weight) {
    return (a + b * weight + c) / (weight + 2.0);
  }

  public static float averageWithWeight(float a, float b, float c, int weight) {
    return (a + b * weight + c) / (weight + 2);
  }

  public static double averageWithWeight(double a, double b, double c, int weight) {
    return (a + b * weight + c) / (weight + 2);
  }
}
